﻿


using Grpc.Net.Client.Configuration;
using InfluxDB.Client.Api.Domain;
using IoTClient;
using IoTClient.Clients.PLC;
using IoTClient.Common.Enums;
using IoTClient.Enums;
using IotClientService.Extensions;
using IotClientService.Handles;
using IotClientService.Models.Hbc;
using IotContract.Dtos.Clients.Equips;
using IotContract.Dtos.Servers;
using IotContract.Dtos.Servers.Pushs;
using IotContract.Enums;
using IotContract.Events;
using IotContract.Interfaces;
using IotContract.Models.Clients;
using Mapster;
using Newtonsoft.Json;
using SyncSocketModuleCore.SyncSocketModule;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using XT.Common.Extensions;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace IotClientService.Operations.plc;
/// <summary>
/// 西门子s7操作
/// </summary>
public class SiemensS7Operation : IEquipOperation<SiemensS7EquipDto>
{
    public event EventHandler<ConnectStatusEventArgs> OnConnectStatusChanged;

    private bool _curConnection;
    private bool _status = false;
    private SiemensS7EquipDto _equipInfo;
    private Task _operationTask;
    private CancellationTokenSource? _cancellationTokenSource;
    //批量读取,不包含String
    private Dictionary<string, DataTypeEnum> _plcs = new Dictionary<string, DataTypeEnum>();

    private List<string> _plcStrs = new List<string>();


 
    public List<EquipTagDto> PLCTagList { get; set; } = new List<EquipTagDto>();

    

    private SiemensClient _client;


    public bool GetConnection()
    {
        return _curConnection;
    }

    public SiemensS7EquipDto GetEquipInfo()
    {
        if (_equipInfo == null) return null;
        // 点位赋值
        var tags = PLCTagList.Adapt<List<PushEquipTag>>();
        _equipInfo.Tags = tags;
        return _equipInfo;
    }

    public bool GetOperationStatus()
    {
        return _status;
    }

    public async Task<bool> Init(SiemensS7EquipDto group, IEnumerable<EquipTagDto> tags = null)
    {
        try
        {
            _equipInfo = group;
            //if (tags == null || tags.Count() == 0)
            //{
            //    return false;
            //}
            _cancellationTokenSource = new CancellationTokenSource();



            PLCTagList = tags.ToList();

            // 解析ipaddress
            var ips = _equipInfo.IPAddress.Split(":");
            if (ips.Length != 2)
            {
                return false;
            }
            var items = Enum.GetNames(typeof(SiemensVersion));

          
            var version = items.FirstOrDefault(x => x.ToString().Contains(_equipInfo.ReadDirection??""));
            if (version == SiemensVersion.None.ToString())
            {
                var error=$"{_equipInfo.ToString()} 初始化失败,原因：解析方向 类型 未定义";

                _equipInfo.Remark = error;
                OnConnectStatusChanged.Invoke(this, new ConnectStatusEventArgs
                {
                    Connect = _curConnection,
                    IsSuccess = false,
                    Message = error
                });
                return false;
            }
            _plcs.Clear();
            _plcStrs.Clear();

            foreach (var tag in PLCTagList)
            {
                var addr = tag.GetIotAddress();
                if (addr.Item2 != 11)
                {
                    _plcs.Add(addr.Item1, (DataTypeEnum)addr.Item2);
                }
                else
                {
                    _plcStrs.Add(addr.Item1);
                }
               
            }

            _client = new SiemensClient(Enum.Parse<SiemensVersion>(version), ips[0], ips[1].To<int>());

            // 测试连通性
            var result= _client.Open();

          
           _curConnection = result.IsSucceed;

            if (!result.IsSucceed)
            {
                var error = $"Error Initial,Not Connect";

                _equipInfo.Remark = error;

                OnConnectStatusChanged.Invoke(this, new ConnectStatusEventArgs
                {
                    Connect = _curConnection,
                    IsSuccess = false,
                    Message = error
                });
                return false;
            }
            else
            {
                OnConnectStatusChanged.Invoke(this, new ConnectStatusEventArgs
                {
                    Connect = true,
                    IsSuccess = true,
                });
            }

            _client.Close();

            _equipInfo.Remark = string.Empty;
            return true;
        }
        catch (Exception ex)
        {
            _curConnection=false;
            var error = $"Error Initial Exception：{ex.Message}";

            _equipInfo.Remark = error;
            
            OnConnectStatusChanged.Invoke(this, new ConnectStatusEventArgs
            {
                Connect = _curConnection,
                IsSuccess = false,
                Message = error
            });
            return false;
        }
    }

    public async Task<bool> OperationStart()
    {
        if (_operationTask != null && _curConnection)
        {
            await OperationStop();

        }

        if (_cancellationTokenSource == null)
        {
            return false;
        }

       

        _operationTask = Task.Factory.StartNew(() => ThreadScheduler(), _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);

        _status = true;
        return true;
    }


    /// <summary>
    /// 任务
    /// </summary>
    /// <returns></returns>
    private async Task ThreadScheduler()
    {
        while (true)
        {
            if (_cancellationTokenSource?.IsCancellationRequested == true)
            {
                break;
            }
            try
            {


                var result=  _client?.Open();

                if (result!=null && result.IsSucceed)
                {
                    _curConnection = true;
                  
                  var reads=  _client?.BatchRead(_plcs);

                    _equipInfo.Remark = reads.Err;
                    if (reads==null || !reads.IsSucceed)
                    {


                        await Task.Delay(_equipInfo.Pooling);
                        continue;                  
                    }


                    foreach (var tag in PLCTagList)
                    {
                        var addr = tag.GetIotAddress().Item1;

                        if (reads.Value.ContainsKey(addr))
                        {
                            SetTagVale(tag, reads.Value[addr]);
                        }
                        else if (_plcStrs.Contains(addr))
                        {
                           var str= _client?.ReadString(addr, tag.DataLength.To<ushort>());
                            if (str!=null && str.IsSucceed)
                            {
                                SetTagVale(tag, Encoding.Default.GetString(str.Value));
                            }
                        }

                    }



                }
                else
                {
                    _equipInfo.Remark = result.Err;
                    if (_curConnection == true)
                    {
                        _curConnection = false;
                        OnConnectStatusChanged?.Invoke(this, new ConnectStatusEventArgs
                        {
                            Connect = false,
                            IsSuccess = false,
                           
                        });
                    }
                    


                }
              



                _client?.Close();
                await Task.Delay(_equipInfo.Pooling);
            }
            catch (Exception exception)
            {
                _curConnection = false;
                OnConnectStatusChanged?.Invoke(this, new ConnectStatusEventArgs
                {
                    Message = exception.Message,
                    Connect = _curConnection,
                    IsSuccess = false

                });
            }


        }
    }
    /// <summary>
    /// 设置点位的值
    /// </summary>
    /// <param name="tag"></param>
    /// <param name="data"></param>
    private  void SetTagVale( EquipTagDto tag, object data)
    {
        
        if (tag.Value != data.ToString())
        {
            tag.OldValue = tag.Value;
            tag.ReadChangeTime = DateTime.Now;

        }
        tag.ReadTime = DateTime.Now;

        //if (tag.DataType == (int)PlcDataType.BIT)
        //{
        //    data = data.ToString() == "False" ? 0 : 1;
        //}

        if (tag.Expression.IsNotNullOrEmpty())
        {
            tag.Value = tag.Expression.GetExpressionsResult(data).ToString();
        }
        else
            tag.Value = data.ToString().Replace("\u0000","");
    }

    public async Task<bool> OperationStop()
    {


       
        _client?.Close();
        _cancellationTokenSource?.Cancel();

        _curConnection = false;

        _status = false;

        OnConnectStatusChanged.Invoke(this, new ConnectStatusEventArgs
        {
            Connect = false,
            IsSuccess = true,
        });
        return true;
    }

    public async Task<bool> WriteRequest(OperationWriteCommand writeInfo)
    {
        // 查找写入点位是否存在
        var tag = PLCTagList.FirstOrDefault(x => x.Id == writeInfo.TagId);
        if (tag == null)
        {
            return false;
        }
        var result= WriteTag(writeInfo, tag);

        if (result!=true)
        {
            await Task.Delay(100);
            result=WriteTag(writeInfo, tag);

            if (result != true)
            {
                WriteTag(writeInfo, tag);
            }

        }
        return true;

    }

    private bool? WriteTag(OperationWriteCommand writeInfo, EquipTagDto? tag)
    {
        var iot = tag.GetIotAddress();
        Result? result = new Result();
        if (_plcs.ContainsKey(iot.Item1))
        {
            var type = (PlcDataType)tag.DataType;
            if (type == PlcDataType.FLOAT)
            {
                result= _client?.Write(iot.Item1, float.Parse(writeInfo.Value));
                
            }
            else if (type == PlcDataType.INT)
            {
                result= _client?.Write(iot.Item1, int.Parse(writeInfo.Value));
                
            }
            else if (type == PlcDataType.INT16)
            {
                result= _client?.Write(iot.Item1, UInt16.Parse(writeInfo.Value));

                

            }
            else if (type == PlcDataType.BYTE)
            {
                result= _client?.Write(iot.Item1, byte.Parse(writeInfo.Value));
               
            }
            else if (type == PlcDataType.BIT)
            {
                if (writeInfo.Value == "1")
                {
                    writeInfo.Value = "true";
                }
                else if (writeInfo.Value == "0")
                {
                    writeInfo.Value="false";
                }
               result= _client?.Write(iot.Item1, bool.Parse(writeInfo.Value));
               
            }
            else if (type == PlcDataType.INT64)
            {
               result= _client?.Write(iot.Item1, Int64.Parse(writeInfo.Value));
               
            }

        }
        else if (_plcStrs.Contains(iot.Item1))
        {
           result= _client?.Write(iot.Item1, writeInfo.Value);
        }
        return result?.IsSucceed;
    }

   

    public void Dispose()
    {
        OperationStop();
    }
}
